package it.infodea.tapestrydea.components.jcr;

import it.infodea.tapestrydea.services.jcr.JcrSessionProviderService;
import it.infodea.tapestrydea.utils.JcrDeaConstants;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import javax.jcr.AccessDeniedException;
import javax.jcr.ItemNotFoundException;
import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.PathNotFoundException;
import javax.jcr.PropertyIterator;
import javax.jcr.RepositoryException;
import javax.jcr.Value;

import org.apache.tapestry5.Asset;
import org.apache.tapestry5.ComponentEventCallback;
import org.apache.tapestry5.ComponentResources;
import org.apache.tapestry5.annotations.BeginRender;
import org.apache.tapestry5.annotations.OnEvent;
import org.apache.tapestry5.annotations.Path;
import org.apache.tapestry5.annotations.Persist;
import org.apache.tapestry5.annotations.Property;
import org.apache.tapestry5.internal.util.Holder;
import org.apache.tapestry5.ioc.annotations.Inject;

public class NodesNavigator {

	public static final String EVENT_TO_HANDLE = "updatecurrentnode";

	public enum NodeType {
		FILE, FOLDER, NODE
	}

	@Inject
	@Path("classpath:/it/infodea/tapestrydea/components/jcr/folder.png")
	private Asset folderImage;

	@Inject
	@Path("classpath:/it/infodea/tapestrydea/components/jcr/file.png")
	private Asset fileImage;

	@Persist
	private Node currentNode;

	@Persist
	private Node upNode;

	@SuppressWarnings("unused")
	@Property
	private int index;

	private List<Node> nodes;

	private Node node;

	@SuppressWarnings("unused")
	@Property
	private PathInfo pathItem;

	@SuppressWarnings("unused")
	@Property
	private PropInfo nodeProperty;

	@Inject
	private ComponentResources _resources;

	@Inject
	private JcrSessionProviderService service;

	@BeginRender
	void prepare() throws RepositoryException {
		if (currentNode == null || "/".equals(currentNode.getPath())) {
			currentNode = service.getSession().getRootNode();
			upNode = null;
		} else {
			upNode = currentNode.getParent();
		}
		updateChilds();
	}

	private void updateChilds() throws RepositoryException {
		NodeIterator nodeIterator = currentNode.getNodes();
		nodes = new ArrayList<Node>();
		while (nodeIterator.hasNext()) {
			nodes.add(nodeIterator.nextNode());
		}
	}

	@OnEvent("nnselectnode")
	Object changeNode(String name) throws PathNotFoundException, RepositoryException {
		currentNode = currentNode.getNode(name);
		return triggerEvent();
	}

	@OnEvent("nnselectpath")
	Object changePath(String path) throws PathNotFoundException, RepositoryException {
		currentNode = (Node) service.getSession().getItem(path);
		return triggerEvent();
	}

	@OnEvent(component = "up")
	Object up() {
		currentNode = upNode;
		return triggerEvent();
	}

	private Object triggerEvent() {
		final Holder<Object> resultHolder = Holder.create();

		ComponentEventCallback<Object> handler = new ComponentEventCallback<Object>() {

			public boolean handleResult(Object result) {
				resultHolder.put(result.toString());
				return true;
			}
		};

		_resources.triggerEvent(EVENT_TO_HANDLE, new Object[] { currentNode, getNodeType(currentNode) }, handler);

		return resultHolder.get();
	}

	private NodeType getNodeType(Node currentNode) {
		String type = null;
		try {
			type = currentNode.getProperty("jcr:primaryType").getString();
		} catch (RepositoryException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		NodeType result = null;
		if (JcrDeaConstants.FILE_PRIMARY_TYPE.equals(type)) {
			result = NodeType.FILE;
		} else if (JcrDeaConstants.FOLDER_PRIMARY_TYPE.equals(type)) {
			result = NodeType.FOLDER;
		} else {
			result = NodeType.NODE;
		}
		return result;
	}

	public boolean isRoot() {
		return upNode == null;
	}

	public boolean isRoot(Node node) throws RepositoryException {
		return "/".equals(node.getPath());
	}

	public List<Node> getNodes() {
		return nodes;
	}

	public Node getNode() {
		return node;
	}

	public void setNode(Node node) {
		this.node = node;
	}

	public List<PropInfo> getNodeProperties() {
		return updateNodeProperties();
	}

	public List<PathInfo> getPathNavigatorList() throws ItemNotFoundException, AccessDeniedException, RepositoryException {
		List<PathInfo> pathInfos = new ArrayList<PathInfo>();
		Node temp = currentNode;
		pathInfos.add(new PathInfo(temp.getName(), temp.getPath()));
		while (!isRoot(temp) && (temp = temp.getParent()) != null) {
			pathInfos.add(new PathInfo(temp.getName(), temp.getPath()));
		}
		Collections.reverse(pathInfos);
		return pathInfos;
	}

	public Asset getImage() throws RepositoryException {
		String primaryType = node.getProperty("jcr:primaryType").getString();
		return JcrDeaConstants.FILE_PRIMARY_TYPE.equals(primaryType) ? fileImage : folderImage;
	}

	private List<PropInfo> updateNodeProperties() {
		List<PropInfo> nodeProperties = new ArrayList<PropInfo>();
		try {
			PropertyIterator properties = node.getProperties();
			javax.jcr.Property property = null;
			Value[] values = null;
			while (properties.hasNext()) {
				property = properties.nextProperty();
				values = (property.getDefinition().isMultiple()) ? property.getValues() : new Value[] { property.getValue() };
				List<String> asList = new ArrayList<String>();
				for (Value value : values) {
					asList.add(value.getString());
				}
				if (values != null) {
					nodeProperties.add(new PropInfo(property.getName(), asList.toString()));
				} else {
					nodeProperties.add(new PropInfo(property.getName(), "null value"));
				}
			}
		} catch (RepositoryException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return nodeProperties;
	}

	public static class PathInfo {
		private String name;

		private String path;

		public PathInfo(String name, String path) {
			super();
			this.name = name;
			this.path = path;
		}

		public String getName() {
			return name;
		}

		public String getPath() {
			return path;
		}
	}

	public static class PropInfo {
		public String propName;

		public String propValue;

		public PropInfo(String propName, String propValue) {
			super();
			this.propName = propName;
			this.propValue = propValue;
		}

		public String getPropName() {
			return propName;
		}

		public void setPropNames(String propName) {
			this.propName = propName;
		}

		public String getPropValue() {
			return propValue;
		}

		public void setPropValues(String propValue) {
			this.propValue = propValue;
		}
	}

}
